package com.seele.tigerwallet.utils;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.DownloadManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.seele.tigerwallet.BuildConfig;
import com.seele.tigerwallet.R;
import com.seele.tigerwallet.model.TokenInfoModel;
import com.seele.tigerwallet.model.UserInfo;
import com.seele.tigerwallet.ui.application.SeeleApplication;
import com.seele.tigerwallet.ui.manager.DaoManager;
import com.seele.tigerwallet.wallet.WalletManager;

import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.crypto.Wallet;
import org.web3j.crypto.WalletFile;
import org.web3j.protocol.ObjectMapperFactory;
import org.web3j.protocol.admin.methods.response.PersonalUnlockAccount;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Pattern;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import cn.jpush.android.api.JPushInterface;

public class Utils {


    public static String getImei(Context context) {
        String imei = "";
        try {
            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(context.TELEPHONY_SERVICE);
            imei = telephonyManager.getDeviceId();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return imei;
    }


    public static String getTransactionSigned(String toAddr, int nonceId, String value, float gasPriceGWei, String password) {
        BigInteger GAS_PRICE = BigInteger.valueOf((int)(gasPriceGWei*100)).multiply(BigInteger.valueOf(1000 * 1000 * 10));//
        Log.i("signed", "getTransactionSigned gas price:" + GAS_PRICE.toString());
        BigInteger GAS_LIMIT = BigInteger.valueOf(21000);
        String toAddress = toAddr;
        String keystore = "";
        try {
            List<TokenInfoModel> tokenInfoModels = DaoManager.getTokenDao().queryBuilder().where().eq("address", UserInfo.get().address).query();
            if (tokenInfoModels != null && tokenInfoModels.size() >= 1) {
                keystore = tokenInfoModels.get(0).keystore;
                String pwd = password + Utils.getSignature().substring(0, 18);
                keystore = CryptoUtils.decrypt(keystore, pwd); //解密
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        String priKey = EthWalletUtil.getPriKey(keystore, password);
        Credentials credentials = Credentials.create(priKey);
        String nonce = nonceId + "";
        BigInteger intNonce = new BigInteger(nonce);
        BigInteger intValue = Convert.toWei(value, Convert.Unit.ETHER).toBigInteger();
        RawTransaction rawTransaction = RawTransaction.createTransaction(
                intNonce, GAS_PRICE, GAS_LIMIT, toAddress, intValue, "");
        //签名Transaction，这里要对交易做签名
        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        String hexValue = Numeric.toHexString(signedMessage);
        return hexValue;
    }


    public static String getSmartContractCode(String toAddress, BigInteger intValue) {
        String methodName = "transfer"; // 函数名是转账
        List<Type> inputParameters = new ArrayList<>();
        List<TypeReference<?>> outputParameters = new ArrayList<>();
        Address tAddress = new Address(toAddress);

        Uint256 value = new Uint256(intValue);
        inputParameters.add(tAddress);
        inputParameters.add(value);

        TypeReference<Bool> typeReference = new TypeReference<Bool>() {
        };
        outputParameters.add(typeReference);

        Function function = new Function(methodName, inputParameters, outputParameters);
        String data = FunctionEncoder.encode(function);
        return data;
    }


    public static String getTokenTransactionSigned(String contractAddress, String toAddress, int nonceId, String value, float gasPriceGWei, int gasLimit, String password) {
        BigInteger GAS_PRICE = BigInteger.valueOf((int)(gasPriceGWei*100)).multiply(BigInteger.valueOf(1000 * 1000 * 10));//
        BigInteger GAS_LIMIT = BigInteger.valueOf(gasLimit);
        String keystore = "";
        try {
            List<TokenInfoModel> tokenInfoModels = DaoManager.getTokenDao().queryBuilder().where().eq("address", UserInfo.get().address).query();
            if (tokenInfoModels != null && tokenInfoModels.size() >= 1) {
                keystore = tokenInfoModels.get(0).keystore;
                String pwd = password + Utils.getSignature().substring(0, 18);
                keystore = CryptoUtils.decrypt(keystore, pwd); //解密
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        String priKey = EthWalletUtil.getPriKey(keystore, password);
        Credentials credentials = Credentials.create(priKey);
        String nonce = nonceId + "";
        BigInteger intNonce = new BigInteger(nonce);
        BigInteger intValue = Convert.toWei(value, Convert.Unit.ETHER).toBigInteger();
        String data = getSmartContractCode(toAddress, intValue);

        RawTransaction rawTransaction = RawTransaction.createTransaction(intNonce, GAS_PRICE, GAS_LIMIT, contractAddress, BigInteger.ZERO, data);
        //签名Transaction，这里要对交易做签名
        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        String hexValue = Numeric.toHexString(signedMessage);
        return hexValue;
    }


    /**
     * *
     *
     * @param v1
     * @param v2
     * @return v1 > v2, return 1
     * v1 < v2, return 2
     * == ,return 0
     * error return -1
     */
    public static int versionCompare(String v1, String v2) {
        Pattern pattern = Pattern.compile("\\d+(\\.\\d+)*");
        if (!pattern.matcher(v1).matches() || !pattern.matcher(v2).matches()) {
            return -1;
        }
        String[] s1 = v1.split("\\.");
        String[] s2 = v2.split("\\.");

        int length = s1.length < s2.length ? s1.length : s2.length;

        for (int i = 0; i < length; i++) {
            int diff = Integer.valueOf(s1[i].trim()) - Integer.valueOf(s2[i].trim());

            if (diff == 0) {
                continue;
            } else {
                return diff > 0 ? 1 : 2;
            }
        }
        return 0;
    }


    /**
     * 下载新版本
     *
     * @param context
     * @param url
     */
    public static void downLoadAPK(Context context, String url, String newVersion) {

        if (TextUtils.isEmpty(url)) {
            return;
        }
        try {
            String serviceString = Context.DOWNLOAD_SERVICE;
            final DownloadManager downloadManager = (DownloadManager) context.getSystemService(serviceString);

            Uri uri = Uri.parse(url);
            DownloadManager.Request request = new DownloadManager.Request(uri);
            request.allowScanningByMediaScanner();
            request.setVisibleInDownloadsUi(true);
            request.setTitle("TigerWallet.apk");
            request.setDescription(String.format("V%s", newVersion));
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            request.setMimeType("application/vnd.android.package-archive");

            File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/seele/", "tigerwallet.apk");
            if (file.exists()) {
                file.delete();
            }
            request.setDestinationInExternalPublicDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/seele/", "tigerwallet.apk");
            Log.i("update", "download path:" + Environment.getExternalStorageDirectory().getAbsolutePath() + "/seele/" + "tigerwallet.apk");
            long refernece = downloadManager.enqueue(request);
            SeeleFileMap.putLong("refernece", refernece);
        } catch (Exception exception) {
            exception.printStackTrace();
            Log.e("update", "update apk failed!");
        }

    }

    public static String getSignature() {
        Context context = SeeleApplication.getInstance();
        PackageManager pm = context.getPackageManager();
        PackageInfo pi;
        StringBuilder sb = new StringBuilder();
        try {
            pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
            Signature[] signatures = pi.signatures;
            for (Signature signature : signatures) {
                sb.append(signature.toCharsString());
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        String md5 = CryptoUtils.MD5(sb.toString());
        return md5;
    }


    private static boolean checkSuFile() {
        Process process = null;
        try {
            //   /system/xbin/which 或者  /system/bin/which
            process = Runtime.getRuntime().exec(new String[]{"which", "su"});
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            if (in.readLine() != null) return true;
            return false;
        } catch (Throwable t) {
            return false;
        } finally {
            if (process != null) process.destroy();
        }
    }

    public static boolean checkRoot() {
        File file = null;
        String[] paths = {"/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su",
                "/system/bin/failsafe/su", "/data/local/su"};
        for (String path : paths) {
            file = new File(path);
            if (file.exists()) return true;
        }
        return false;
    }

    private static boolean isCanExecute(String filePath) {
        java.lang.Process process = null;
        try {
            process = Runtime.getRuntime().exec("ls -l " + filePath);
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String str = in.readLine();
            if (str != null && str.length() >= 4) {
                char flag = str.charAt(3);
                if (flag == 's' || flag == 'x') {
                    Runtime.getRuntime().exec("su ");
                    return true;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (process != null) {
                process.destroy();
            }
        }
        return false;
    }

    public static boolean isExistMainActivity(Class<?> activity){
        Context context = SeeleApplication.getInstance();
        Intent intent = new Intent(context, activity);
        ComponentName cmpName = intent.resolveActivity(context.getPackageManager());
        boolean flag = false;
        if (cmpName != null) { // 说明系统中存在这个activity
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.RunningTaskInfo> taskInfoList = am.getRunningTasks(10);  //获取从栈顶开始往下查找的10个activity
            for (ActivityManager.RunningTaskInfo taskInfo : taskInfoList) {
                if (taskInfo.baseActivity.equals(cmpName)) { // 说明它已经启动了
                    flag = true;
                    break;  //跳出循环，优化效率
                }
            }
        }
        return flag;
    }


    public static String getJPushId() {
        String pushid = SeeleFileMap.getString(Constants.KEY_JPUSH_ID);
        if (TextUtils.isEmpty(pushid)) {
            pushid = JPushInterface.getRegistrationID(SeeleApplication.getInstance());
        }
        return pushid;
    }

    public static String formatTransferTime(String time) {
        String formatTime = "";
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date = (Date) sdf.parse(time);
            long longTime = date.getTime();

            SimpleDateFormat sdf1= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            java.util.Date date1 = new Date(longTime);
            String str = sdf1.format(date1);
            System.out.println(str);

            long currentTime = System.currentTimeMillis();
            long spitTime = currentTime - longTime;
            if (spitTime < 60 * 1000) {
                formatTime = SeeleApplication.getInstance().getString(R.string.time_just);
            } else if (spitTime < 60 * 60 * 1000) {
                formatTime = String.format("%d%s",spitTime / 60 / 1000, SeeleApplication.getInstance().getString(R.string.time_second));
            } else if (spitTime < 24 * 60 * 60 * 1000) {
                formatTime = String.format("%d%s",spitTime / 60 / 60 / 1000, SeeleApplication.getInstance().getString(R.string.time_hour));
            } else if (spitTime < 7 * 24 * 60 * 60 * 1000) {
                formatTime = String.format("%d%s",spitTime / 24 / 60 / 60 /1000, SeeleApplication.getInstance().getString(R.string.time_day));
            } else {
                formatTime = time;
            }
        } catch (Exception e) {
            formatTime = time;
            Log.i("psp", "formatTransferTime exp:" + e.getMessage());
        }
        return formatTime;
    }

    public static boolean isUnSafeApk() {
        String currentMd5 = getSignature();
        if (!TextUtils.isEmpty(currentMd5) && currentMd5.equals(Constants.signMd5)) {
            return  false;
        }
        return true;
    }

}